package model;

import java.awt.geom.Point2D;
import java.util.ArrayList;

import utils.MathUtils;


public class VisibilityChecker {
	private Team leftTeam;
	private Team rightTeam;
	private Player[] leftPlayers;
	private Player[] rightPlayers;
	private Tree[] trees;
	
	public void update(Team leftTeam, Team rightTeam, Tree[] trees) {
		this.leftTeam = leftTeam;
		this.rightTeam = rightTeam;
		this.trees = trees;
		this.leftPlayers = leftTeam.getPlayers();
		this.rightPlayers = rightTeam.getPlayers();
		updateVisibility();
	}
	
	private void updateVisibility() {	
		//see which players on the right team are detected by players on the left team
		ArrayList<Player> undetectedRightTeamPlayers = new ArrayList<Player>();
		for (int i = 0; i < rightPlayers.length; i++) {
			undetectedRightTeamPlayers.add(rightPlayers[i]);
		} 
		for (Player player: leftPlayers) {
			if (!rightTeam.getJail().contains(player.getPosition())) {
				ArrayList<Player> enemiesInVisionArc = getEnemyPlayersInVisionArc(player, undetectedRightTeamPlayers);
				for (int i = 0; i < enemiesInVisionArc.size() && undetectedRightTeamPlayers.size() > 0; i++) {
					Player detectedPlayer = enemiesInVisionArc.get(i);
					undetectedRightTeamPlayers.remove(detectedPlayer);
					enemiesInVisionArc.get(i).setVisibility(true);
				}
			}
		}
		for (int i = 0; i < undetectedRightTeamPlayers.size(); i++) {
			undetectedRightTeamPlayers.get(i).setVisibility(false);
		} 
		
		//see which players on the left team are detected by players on the right team
		ArrayList<Player> undetectedLeftTeamPlayers = new ArrayList<Player>();
		for (int i = 0; i < leftPlayers.length; i++) {
			undetectedLeftTeamPlayers.add(leftPlayers[i]);
		} 
		for (Player player: rightPlayers) {
			if (!leftTeam.getJail().contains(player.getPosition())) {
				ArrayList<Player> enemiesInVisionArc = getEnemyPlayersInVisionArc(player, undetectedLeftTeamPlayers);
				for (int i = 0; i < enemiesInVisionArc.size() && undetectedLeftTeamPlayers.size() > 0; i++) {
					Player detectedPlayer = enemiesInVisionArc.get(i);
					undetectedLeftTeamPlayers.remove(detectedPlayer);
					enemiesInVisionArc.get(i).setVisibility(true);
				}
			}
		}
		for (int i = 0; i < undetectedLeftTeamPlayers.size(); i++) {
			undetectedLeftTeamPlayers.get(i).setVisibility(false);
		} 
	}

	private ArrayList<Player> getEnemyPlayersInVisionArc(Player player,
			ArrayList<Player> undetectedEnemyPlayers) {
		ArrayList<Player> playersInArc = new ArrayList<Player>();
		for (int i = 0; i < undetectedEnemyPlayers.size(); i++) {
			Player enemyPlayer = undetectedEnemyPlayers.get(i);
			if (objectInVisionArc(player, enemyPlayer)) {
				playersInArc.add(enemyPlayer);
			}
		}
		return playersInArc;
	}

	private boolean objectInVisionArc(IVisionObject observer, IVisionObject object) {
		Point2D observerPosition = observer.getPosition();
		Point2D objectPosition = object.getPosition();
		if(observerPosition.distance(objectPosition) <= GameConstants.PLAYER_VIEW_DIST + object.getRadius()) {
			Point2D[] tangentPoints = MathUtils.getTangentPoints(
					observerPosition, objectPosition, object.getRadius());
			if (pointInVisionArc(observer, tangentPoints[0]) || pointInVisionArc(observer, tangentPoints[1])){
				return true;
			} else {
				return false;
			}
		}
		else {
			return false;
		}
	}

	private boolean pointInVisionArc(IVisionObject observer, Point2D point) {
		Point2D observerPosition = observer.getPosition();

		double angleToObject = Math.toDegrees(Math.atan2(
				point.getX()-observerPosition.getX(), point.getY()-observerPosition.getY()));
		while (angleToObject < 0) angleToObject += 360;
		while (angleToObject > 360) angleToObject -= 360;
		double angleDifference = observer.getOrientation() - angleToObject;
		if (Math.abs(angleDifference) <= GameConstants.PLAYER_FIELD_OF_VIEW/2) {
			return true;
		} else {
			return false;
		}	

	}

}
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
	
//	 private Line2D extendLine(Line2D line, double magnitude){
//		
//		double hypotenuse1 = Point2D.distance(0, 0, line.getX2(), line.getY2());
//		double adjacent = line.getX2() - line.getX1();
//		double opposite = line.getY2() - line.getY1();
//		double newXLength = magnitude/hypotenuse1*adjacent;
//		double newYLength = magnitude/hypotenuse1*opposite;
//		
//		return new Line2D.Double(line.getX1(), line.getY1(), newXLength, newYLength);
//	}
//	 	private void updateVisibility(){
//		//Start with left team
//		Flag rightTeamFlag = rightTeam.getFlag();
//		for( int i = GameConstants.NUM_PLAYERS_PER_SIDE - 1; i > 0; i--){
//			Player tPlayer = leftTeam.getPlayer(i);
//			//Set Flag Visibility  --  Create a line from the flag to visible players
//			if( rightTeamFlag.getPosition().distance(tPlayer.getPosition()) <= GameConstants.PLAYER_VIEW_DIST ){
//				Line2D lineToFlag = new Line2D.Double(tPlayer.getPosition().getX(), tPlayer.getPosition().getY(),
//						rightTeamFlag.getPosition().getX(), rightTeamFlag.getPosition().getY() );
//
//
//
//				if ( this.inFieldOfView(tPlayer, rightTeamFlag.getPosition(), GameConstants.FLAG_RADIUS)){
//					if(getBlockingTrees(lineToFlag).size() > 0  || getBlockingPlayers(lineToFlag).size() > 0 ){ //if anything blocks the flag is not visible due to 0 radius
//						rightTeamFlag.setVisibility(true);
//					}else{
//						rightTeamFlag.setVisibility(false);
//					}
//				}
//
//			}
//			// Set Player Visibility -- Create a line to each opposing player
//			for(int j = 0; j < GameConstants.NUM_PLAYERS_PER_SIDE ; j++){
//				//check if specific player is within current player vision arc
//				Player opposingPlayer = leftTeam.getPlayer(j);
//				if( opposingPlayer.getPosition().distance(tPlayer.getPosition()) <= GameConstants.PLAYER_VIEW_DIST ){
//					Line2D line = new Line2D.Double(tPlayer.getPosition().getX(), tPlayer.getPosition().getY(),
//							opposingPlayer.getPosition().getX(), opposingPlayer.getPosition().getY() );
//					ArrayList<Tree> blockingTrees = getBlockingTrees(line);
//					ArrayList<Player> blockingPlayers = getBlockingPlayers(line);					
//				}
//			}
//		}
//		Flag leftTeamFlag = leftTeam.getFlag();
//	}
//
//	private Point2D[] getVisionPoints(Player player, Point2D point, int radius){	
//		Point2D[] points = new Point2D[2];
//		
//		double opp = radius; // diagram: hyp of small triangle, opp of big triangle		
//		
//		double adj = point.distance(player.getPosition()); //adj of big triangle
//		double theta = Math.atan(opp/adj); //angle of both triangles
//		
//		//for small triangle past here
//		double hyp = opp;
//		double addRelX = Math.cos(theta)*hyp;
//		double addRelY = Math.sin(theta)*hyp;
//		
//		if(point.getX() > player.getPosition().getX() && point.getY() > player.getPosition().getY()){ //quadrant 1
//			points[0] = new Point2D.Double(point.getX()+addRelX, point.getY()-addRelY);
//			points[1] = new Point2D.Double(point.getX()-addRelX, point.getY()+addRelY);		
//		}else if(point.getX() > player.getPosition().getX() && point.getY() < player.getPosition().getY()){ //quadrant 2
//			points[0] = new Point2D.Double(point.getX()+addRelX, point.getY()+addRelY);
//			points[1] = new Point2D.Double(point.getX()-addRelX, point.getY()-addRelY);		
//		}else if(point.getX() < player.getPosition().getX() && point.getY() < player.getPosition().getY()){ //quadrant 3
//			points[0] = new Point2D.Double(point.getX()+addRelX, point.getY()-addRelY);
//			points[1] = new Point2D.Double(point.getX()-addRelX, point.getY()+addRelY);		
//		}else if(point.getX() < player.getPosition().getX() && point.getY() > player.getPosition().getY()){ //quadrant 4
//			points[0] = new Point2D.Double(point.getX()+addRelX, point.getY()+addRelY);
//			points[1] = new Point2D.Double(point.getX()-addRelX, point.getY()-addRelY);		
//		}else if(point.getX() == player.getPosition().getX()){//on x-axis
//			points[0] = new Point2D.Double(point.getX(), point.getY()+radius);
//			points[1] = new Point2D.Double(point.getX(), point.getY()-radius);
//		}else if(point.getY() == player.getPosition().getY()){ //on y-axis
//			points[0] = new Point2D.Double(point.getX()+radius, point.getY());
//			points[1] = new Point2D.Double(point.getX()-radius, point.getY());
//		}
//		
//		return points;		
//	}
//	
//		/**
//	 * Determines if any players are potential blockers for the line
//	 * @param line
//	 * @return
//	 **/
//	private ArrayList<Player> getBlockingPlayers(Line2D line){
//		ArrayList<Player> blockingPlayers = new ArrayList<Player>();
//		for(int i = 0; i < GameConstants.NUM_PLAYERS_PER_SIDE; i++){
//			if(GameConstants.PLAYER_RADIUS >= line.ptSegDist(leftTeam.getPlayer(i).getPosition())){
//				blockingPlayers.add(leftTeam.getPlayer(i));
//			}
//
//			if(GameConstants.PLAYER_RADIUS >= line.ptSegDist(rightTeam.getPlayer(i).getPosition())){
//				blockingPlayers.add(rightTeam.getPlayer(i));
//			}
//		}
//		return blockingPlayers;
//	}
//	
//	/**
//	 * Determines if an object is within a players field of view
//	 * @param tPlayer - 
//	 * @param point - the objects center point
//	 * @param radius - the objects radius
//	 * @return true if the object is within the players field of view
//	**/
//	 
//	private boolean inFieldOfView(Player tPlayer, Point2D point, double radius){
//		boolean inFOV = false;
//		//From player to his max view dist straight ahead.
//		Line2D lineOfView = new Line2D.Double( /* Polar X */ tPlayer.getPosition().getX() + GameConstants.PLAYER_VIEW_DIST * Math.cos(tPlayer.getOrientation())
//				, /* Polar Y */ tPlayer.getPosition().getY() + GameConstants.PLAYER_VIEW_DIST * Math.cos(tPlayer.getOrientation()), 
//				tPlayer.getPosition().getX(), tPlayer.getPosition().getY() );
//		//Take the distance of the inner radius to find the perp dist and use to calculate an angle, if angle is< 60 its in FOV
//		if ( Math.abs( Math.asin(lineOfView.ptSegDist(point)-radius)/tPlayer.getPosition().distance(point)) < 60){
//			inFOV = true;
//		}		
//		return inFOV;
//	}
//	
//	private ArrayList<Tree> getBlockingTreeArray(Tree[] trees2, Player player) {
//		ArrayList<Tree> blockingTreeArray = new ArrayList<Tree>();
//		for (Tree tree : this.trees) {
//			Point2D treePosition = tree.getPosition();
//			if (inFieldOfView(player, treePosition, GameConstants.TREE_RADIUS)) {
//				blockingTreeArray.add(tree);
//			}
//		}
//		return blockingTreeArray;
//	}
//	
//	private ArrayList<Player> getBlockingPlayerArray(Player[] rightPlayers, Player p) {
//		ArrayList<Player> blockingPlayers = new ArrayList<Player>();
//		for (Player player : rightPlayers) {
//			if (inFieldOfView(p, player.getPosition(), GameConstants.PLAYER_RADIUS)) {
//				blockingPlayers.add(player);
//			}
//		}
//		return blockingPlayers;
//	}	
//	
//	private void removeBlockingTreesFromVision(Area visionAreaArc, ArrayList<Tree> blockingTreeArray, Point2D playersPosition, double visionStartingAngle){
//		for(Tree tree: this.trees){
//			Point2D treePosition = tree.getPosition();
//			double treeAngle = Math.atan(GameConstants.TREE_RADIUS/treePosition.distance(playersPosition));
//			
//			Line2D line1 = new Line2D.Double(playersPosition.getX(), playersPosition.getY(),
//					playersPosition.getX() + Math.cos(visionStartingAngle)*GameConstants.PLAYER_VIEW_DIST,
//					playersPosition.getY() + Math.sin(visionStartingAngle)*GameConstants.PLAYER_VIEW_DIST);
//			
//			Line2D line2 = new Line2D.Double(playersPosition.getX(), playersPosition.getY(),
//					treePosition.getX(), treePosition.getY());
//			
//			double startingAngle = visionStartingAngle + MathUtils.dotProduct(line1, line2) - treeAngle;
//			double radiusToTree = playersPosition.distance(treePosition);
//			
//			Arc2D bigVisionArc = new Arc2D.Double(playersPosition.getX() - GameConstants.PLAYER_VIEW_DIST,
//					playersPosition.getY() - GameConstants.PLAYER_VIEW_DIST,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					startingAngle,
//					2*treeAngle,
//					Arc2D.PIE);
//			
//			Arc2D littleVisionArc = new Arc2D.Double(playersPosition.getX() - radiusToTree,
//					playersPosition.getY() - radiusToTree,
//					radiusToTree*2,
//					radiusToTree*2,
//					startingAngle,
//					2*treeAngle,
//					Arc2D.PIE);
//			
//			
//			
////			Ellipse2D circle = new Ellipse2D.Double(tree.getX() - GameConstants.TREE_RADIUS,
////					tree.getY() - GameConstants.TREE_RADIUS,
////					GameConstants.TREE_RADIUS*2,
////					GameConstants.TREE_RADIUS*2);
////			
//			Area bigVisionArea = new Area(bigVisionArc);
//			Area littleVisionArea = new Area(littleVisionArc);
////			Area circleArea = new Area(circle);
//			
//			bigVisionArea.subtract(littleVisionArea);
////			bigVisionArea.subtract(circleArea);
//			visionAreaArc.subtract(bigVisionArea);
//		}
//	}
//	private void removeBlockingPlayersFromVision(Area visionAreaArc, ArrayList<Player> players, Point2D playersPosition, double visionStartingAngle){
//		for(Player player : players){
//			double playerAngle = Math.atan(GameConstants.TREE_RADIUS/player.getPosition().distance(playersPosition));
//				
//			Line2D line1 = new Line2D.Double(playersPosition.getX(), playersPosition.getY(),
//					playersPosition.getX() + Math.cos(visionStartingAngle)*GameConstants.PLAYER_VIEW_DIST,
//					playersPosition.getY() + Math.sin(visionStartingAngle)*GameConstants.PLAYER_VIEW_DIST);
//			
//			Line2D line2 = new Line2D.Double(playersPosition.getX(), playersPosition.getY(),
//					player.getPosition().getX(), player.getPosition().getY());
//			
//			double startingAngle = visionStartingAngle + MathUtils.dotProduct(line1, line2) - playerAngle;
//			double radiusToPlayer = playersPosition.distance(player.getPosition());
//			
//			Arc2D bigVisionArc = new Arc2D.Double(playersPosition.getX() - GameConstants.PLAYER_VIEW_DIST,
//					playersPosition.getY() - GameConstants.PLAYER_VIEW_DIST,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					startingAngle,
//					2*playerAngle,
//					Arc2D.PIE);
//			
//			Arc2D littleVisionArc = new Arc2D.Double(playersPosition.getX() - radiusToPlayer,
//					playersPosition.getY() - radiusToPlayer,
//					radiusToPlayer*2,
//					radiusToPlayer*2,
//					startingAngle,
//					2*playerAngle,
//					Arc2D.PIE);
//			
////			Ellipse2D circle = new Ellipse2D.Double(player.getPosition().getX() - GameConstants.PLAYER_RADIUS,
////					player.getPosition().getY() - GameConstants.PLAYER_RADIUS,
////					GameConstants.PLAYER_RADIUS*2,
////					GameConstants.PLAYER_RADIUS*2);
//			
//			Area bigVisionArea = new Area(bigVisionArc);
//			Area littleVisionArea = new Area(littleVisionArc);
////			Area circleArea = new Area(circle);
//			
//			bigVisionArea.subtract(littleVisionArea);
////			bigVisionArea.subtract(circleArea);
//			visionAreaArc.subtract(bigVisionArea);
//		}
//	}
//	private void setVisibilityOnEntities(Area visionArea,
//			Player[] rightPlayers, Flag flag) {
//		for (Player player : rightPlayers) {
//			if (!player.isPlayerIsVisible()) {
//				Ellipse2D circle = new Ellipse2D.Double(player.getPosition()
//						.getX()
//						- GameConstants.PLAYER_RADIUS, player.getPosition()
//						.getY()
//						- GameConstants.PLAYER_RADIUS,
//						2 * GameConstants.PLAYER_RADIUS,
//						2 * GameConstants.PLAYER_RADIUS);
//
//				Area playerArea = new Area(circle);
//				playerArea.intersect(visionArea);
//				if (!playerArea.isEmpty()) {
//					player.setVisibility(true);
//				}
//			}
//		}
//		
//		if(visionArea.contains(flag.getPosition())){
//			flag.setVisibility(true);
//		}
//		
//	}
//	private void updateVisibilityShapeMethod(){
//		Player[] leftPlayers = leftTeam.getPlayers();		
//		Player[] rightPlayers = rightTeam.getPlayers();
//		
//		// Reset all players to invisible
//		Player[] allPlayers = getAllPlayers();
//		for(Player player : allPlayers){
//			player.setVisibility(false);
//		}
//		leftTeam.getFlag().setVisibility(false);
//		rightTeam.getFlag().setVisibility(false);
//		
//		//start with left team		
//		for(int i = 0; i < GameConstants.NUM_PLAYERS_PER_SIDE; i++){
//			Player player = leftPlayers[i];
//			Arc2D visionArc = new Arc2D.Double(player.getPosition().getX() - GameConstants.PLAYER_VIEW_DIST,
//					player.getPosition().getY() - GameConstants.PLAYER_VIEW_DIST,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					player.getOrientation() - 150,
//					120,
//					Arc2D.PIE);
//			
//			Area visionArcArea = new Area(visionArc);
//			ArrayList<Tree> blockingTreeArray = getBlockingTreeArray(this.trees, player);
//			ArrayList<Player> blockingPlayerArray = getBlockingPlayerArray(rightPlayers, player);
//
//			removeBlockingTreesFromVision(visionArcArea, blockingTreeArray, player.getPosition(), visionArc.getAngleStart());
//			removeBlockingPlayersFromVision(visionArcArea, blockingPlayerArray, player.getPosition(), visionArc.getAngleStart());
//			
//			setVisibilityOnEntities(visionArcArea, rightPlayers, rightTeam.getFlag());
//		}
//		//right team visibility check
//		for(int i = 0; i < GameConstants.NUM_PLAYERS_PER_SIDE; i++){
//			Player player = rightPlayers[i];
//			Arc2D visionArc = new Arc2D.Double(player.getPosition().getX() - GameConstants.PLAYER_VIEW_DIST,
//					player.getPosition().getY() - GameConstants.PLAYER_VIEW_DIST,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					GameConstants.PLAYER_VIEW_DIST*2,
//					player.getOrientation() - 150,
//					120,
//					Arc2D.PIE);
//			
//			Area visionArcArea = new Area(visionArc);
//			ArrayList<Tree> blockingTreeArray = getBlockingTreeArray(this.trees, player);
//			ArrayList<Player> blockingPlayerArray = getBlockingPlayerArray(leftPlayers, player);
//
//			removeBlockingTreesFromVision(visionArcArea, blockingTreeArray, player.getPosition(), visionArc.getAngleStart());
//			removeBlockingPlayersFromVision(visionArcArea, blockingPlayerArray, player.getPosition(), visionArc.getAngleStart());
//			
//			setVisibilityOnEntities(visionArcArea, leftPlayers, leftTeam.getFlag());
//		}
//	
//	}
